/* 
 * File:   main.cpp
 * Author: Marco Ambu
 *
 * Created on September 24, 2010, 10:17 AM
 */

/*
  Classifier application
  Copyright (C) 2010 Marco Ambu

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <iostream>
#include <pr/TrainingSet.h>
#include <pr/Trainer.h>
#include <pr/Tester.h>
#include <pr/NearestMeanClassifier.h>
#include <pr/NearestNeighborClassifier.h>
#include <pr/TrainingSetReader.h>
#include <pr/PatternOperation.h>
#include <util/PgmImage.h>
#include <util/RawImage.h>
#include <util/Utility.h>

int main(int argc, char** argv)
{
  std::string trainingConfigFile;
  std::size_t imageWidth, imageHeight;
  if (argc > 3)
  {
    trainingConfigFile = argv[1];
    imageWidth = util::Utility::fromString<std::size_t>(argv[2]);
    imageHeight = util::Utility::fromString<std::size_t>(argv[3]);
  }
  else
  {
    std::cout << "Usage: " << argv[0] << "PatternSetList trainingConfigFile imageWidth imageHeight [rank = 3]" << std::endl;
    return -1;
  }
  std::size_t rank = 3;
  if (argc > 4)
    rank = util::Utility::fromString<std::size_t>(argv[4]);

  std::size_t numFeatures = imageWidth*imageHeight;//180*200;

  std::cout << " * * Statistical Classifier * *" << std::endl << std::endl;
//  std::cout << "- Number of classes: "<< numClasses << std::endl;
  std::cout << "- Number of features: "<< numFeatures << "(" << imageWidth << "x"<< imageHeight << ")" << std::endl;

  std::cout << "> Preparing training set..." << std::endl;
  pr::TrainingSet trainingSet(numFeatures);
  pr::TrainingSetReader r(trainingSet);
  if (!trainingConfigFile.empty())
    r.readConfigFromFile(trainingConfigFile);
  r.read(pr::PatternReader<util::RawImage>(util::RawImage(imageWidth,imageHeight)));
//  r.read(pr::PatternReader<util::PgmImage>(util::PgmImage()));
  pr::PatternOperation::Normalize2 patternOperation;
  trainingSet.transform(patternOperation);
  std::cout << "Training set -> pattern count: " << trainingSet.patternCount() << std::endl;
  std::cout << "Training set -> class count: " << trainingSet.classCount() << std::endl;
//  util::Formatter::getInstance().toStream(trainingSet.mean(), std::cout);
  std::cout << "> ...completed." << std::endl;

  std::cout << "> Using NearestMean classifier" << std::endl;
  pr::NearestMeanClassifier classifier(trainingSet.classCount(), numFeatures);
//  std::cout << "> Using NearestNeighbor classifier" << std::endl;
//  pr::NearestNeighborClassifier classifier(numClasses, numFeatures);

  std::cout << "> Training..." << std::endl;
  pr::Trainer trainer(classifier, trainingSet);
  trainer.train();
  std::cout << "> ...completed." << std::endl;

//  trainingSet.clear();  // REUSING FOR TEST SET

  pr::TrainingSet& testSet = trainingSet; // REUSING FOR TEST SET
//  pr::TrainingSet testSet;
  std::cout << "> Preparing test set..." << std::endl;
  
  std::cout << "Training set count: " << testSet.patternCount() << std::endl;
  std::cout << "> ...completed." << std::endl;

  std::cout << "> Testing..." << std::endl;
  pr::Tester tester(classifier, testSet);
  tester.test();
  const pr::TestResult& result = tester.getResult();
  std::cout << "Errors: " << result.errorCount() << "/" << testSet.patternCount() << std::endl;
  std::cout << "Error rate: " << result.errorRate() << std::endl;
  for (std::size_t i = 0; i < result.errorCount(); ++i)
  {
    std::size_t computedClassId;
    pr::UniquePattern p = result.getError(i, computedClassId);
    std::cout << "Test pattern id: " << p.Id << " expected classId: " << p.ClassId << " computed classId: " << computedClassId << std::endl;
  }
  std::cout << "> ...completed." << std::endl;

  std::cout << "> Computing rank for the first " << rank << "..." << std::endl;

  tester.rank(rank);
//  result = tester.getResult();
  std::cout << "Ranking errors: " << result.errorCount() << "/" << testSet.patternCount() << std::endl;
  std::cout << "Ranking error rate: " << result.errorRate() << std::endl;
  for (std::size_t i = 0; i < result.errorCount(); ++i)
  {
    std::size_t computedClassId;
    pr::UniquePattern p = result.getError(i, computedClassId);
//    std::cout << "Test pattern id: " << p.Id << std::endl;
  }
  std::cout << "> ...completed." << std::endl;

  return 0;
}

